home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Celestin Apprentice 7
/
Apprentice-Release7.iso
/
Environments
/
PowerLisp 2.01
/
Supplemental Documentation
/
Documentation
/
Chapter 29. Conditions
< prev
next >
Wrap
Text File
|
1995-03-28
|
128KB
|
3,077 lines
Common Lisp the Language, 2nd Edition
-------------------------------------------------------------------------------
29. Conditions
By Kent M. Pitman
[change_begin]
PREFACE: The language defined by the first edition contained an enormous
lacuna: although facilities were specified for signaling errors, no means was
defined for handling errors. This occurred not through neglect of the issue,
but because this part of the Lisp language generally was in a state of flux.
There were several proposals at the time. The committee, finding that it could
not agree on any one proposal, agreed to disagree and omit error handling from
Common Lisp for the time being. This defect has now been addressed.
X3J13 voted in June 1988 (CONDITION-SYSTEM) to adopt the Common Lisp
Condition System as a part of the forthcoming draft Common Lisp standard. X3J13
voted in March 1989 (ZLOS-CONDITIONS) to amend the specification of
conditions to integrate them with the Common Lisp Object System (see chapter
28). X3J13 voted in June 1989 (CONDITION-RESTARTS) to amend the specification
of restarts in certain ways. These amendments have been incorporated here with
little further comment.
This chapter presents the bulk of the Common Lisp Condition System proposal,
written by Kent M. Pitman and amended by X3J13. I have edited it only very
lightly to conform to the overall style of this book and have inserted a small
number of bracketed remarks identified by the initials GLS. Please see the
Acknowledgments to this second edition for the author's acknowledgments to
others who contributed to the Condition System proposal.
-Guy L. Steele Jr.
[change_end]
-------------------------------------------------------------------------------
* Introduction
* Changes in Terminology
* Survey of Concepts
o Signaling Errors
o Trapping Errors
o Handling Conditions
o Object-Oriented Basis of Condition Handling
o Restarts
o Anonymous Restarts
o Named Restarts
o Restart Functions
o Comparison of Restarts and Catch/Throw
o Generalized Restarts
o Interactive Condition Handling
o Serious Conditions
o Non-Serious Conditions
o Condition Types
o Signaling Conditions
o Resignaling Conditions
o Condition Handlers
o Printing Conditions
* Program Interface to the Condition System
o Signaling Conditions
o Assertions
o Exhaustive Case Analysis
o Handling Conditions
o Defining Conditions
o Creating Conditions
o Establishing Restarts
o Finding and Manipulating Restarts
o Warnings
o Restart Functions
o Debugging Utilities
* Predefined Condition Types
-------------------------------------------------------------------------------
29.1. Introduction
[change_begin]
Often we find it useful to describe a function in terms of its behavior in
``normal situations.'' For example, we may say informally that the function +
returns the sum of its arguments or that the function read-char returns the
next available character on a given input stream.
Sometimes, however, an ``exceptional situation'' will arise that does not fit
neatly into such descriptions. For example, + might receive an argument that is
not a number, or read-char might receive as a single argument a stream that has
no more available characters. This distinction between normal and exceptional
situations is in some sense arbitrary but is often very useful in practice.
For example, suppose a function f were defined to allow only integer arguments
but also guaranteed to detect and signal an error for non-integer arguments.
Such a description is in fact internally inconsistent (that is, paradoxical)
because the function's behavior is well-defined for non-integers. Yet we would
not want this annoying paradox to force description of f as a function that
accepts any kind of argument (just in case f is being called only as a quick
way to signal an error, for example). Using the normal/exceptional distinction,
we can say clearly that f accepts integers in the normal situation and signals
an error in exceptional situations. Moreover, we can say that when we refer to
the definition of a function informally, it is acceptable to speak only of its
normal behavior. For example, we can speak informally about f as a function
that accepts only integers without feeling that we are committing some awful
fraud.
Not all exceptional situations are errors. For example, a program that is
directing the typing of a long line of text may come to an end-of-line. It is
possible that no real harm will result from failing to signal end-of-line to
its caller because the operating system will simply force a carriage return on
the output device, which will continue typing on the next line. However, it may
still be interesting to establish a protocol whereby the printing program can
inform its caller of end-of-line exceptions. The caller could then opt to deal
with these situations in interesting ways at certain times. For example, a
caller might choose to terminate printing, obtaining an end-of-line truncation.
The important thing, however, is that the failure of the caller to provide
advice about the situation need not prevent the printer program from operating
correctly.
Mechanisms for dealing with exceptional situations vary widely. When an
exceptional situation is encountered, a program may attempt to handle it by
returning a distinguished value, returning an additional value, setting a
variable, calling a function, performing a special transfer of control, or
stopping the program altogether and entering the debugger.
For the most part, the facilities described in this chapter do not introduce
any fundamentally new way of dealing with exceptional situations. Rather, they
encapsulate and formalize useful patterns of data and control flow that have
been seen to be useful in dealing with exceptional situations.
A proper conceptual approach to errors should perhaps begin from first
principles, with a discussion of conditions in general, and eventually work up
to the concept of an error as just one of the many kinds of conditions.
However, given the primitive state of error-handling technology, a proper
buildup may be as inappropriate as requiring that a beggar learn to cook a
gourmet meal before being allowed to eat. Thus, we deal first with the
essentials-error handling-and then go back later to fill in the missing
details.
[change_end]
-------------------------------------------------------------------------------
29.2. Changes in Terminology
[change_begin]
In this section, we introduce changes to the terminology defined in section
1.2.4.
A condition is an interesting situation in a program that has been detected and
announced. Later we allow this term also to refer to objects that programs use
to represent such situations.
An error is a condition in which normal program execution may not continue
without some form of intervention (either interactively by the user or under
some sort of program control, as described below).
The process by which a condition is formally announced by a program is called
signaling. The function signal is the primitive mechanism by which such
announcement is done. Other abstractions, such as error and cerror, are built
using signal.
The first edition is ambiguous about the reason why a particular program action
``is an error.'' There are two principal reasons why an action may be an error
without being required to signal an error:
* Detecting the error might be prohibitively expensive.
For example, (+ nil 3) is an error. It is likely that the designers of
Common Lisp believed this would be an error in all implementations but
felt it might be excessively expensive to detect the problem in compiled
code on stock hardware, so they did not require that it signal an error.
* Some implementations might implement the behavior as an extension.
For example, (loop for x from 1 to 3 do (print x)) is an error because
loop is not defined to take atoms in its body. In fact, however, some
implementations offer an extension that makes this well-defined. In order
to leave room for such extensions, the first edition used the ``is an
error'' terminology to keep implementors from being forced to signal an
error in the extended implementations.
[This example was written well before the vote by X3J13 in January 1989 to
add exactly this extension to the forthcoming draft standard (see chapter
26).-GLS]
In this chapter, we use the following terminology. [Compare this to the
terminology presented in section 28.1.1.-GLS]
* If the signaling of a condition or error is part of a function's contract
in all situations, we say that it ``signals'' or ``must signal'' that
condition or error.
* If the signaling of a condition or error is optional for some important
reason (such as performance), we say that the program ``might signal''
that condition or error. In this case, we are defining the operation to be
illegal in all implementations, but allowing some implementations to fail
to detect the error.
* If an action is left undefined for the sake of implementation-dependent
extension, we say that it ``is undefined'' or ``has undefined effect.''
This means that it is not possible to depend portably upon the effects of
that action. A program that has undefined effect may enter the debugger,
transfer control, or modify data in unpredictable ways.
* In the special case where only the return value of an operation is not
well defined but any side effect and transfer-of-control behavior is well
defined, we say that it has ``undefined value.'' In this case, the number
and nature of the return values is not defined, but the function can
reasonably be expected to return. It is worth noting that under this
description, there are some (though not many) legitimate ways in which
such return value(s) can be used. For example, if the function foo has no
side effects and undefined value, the expression (length (list (foo))) is
completely well defined even for portable code. However, the effect of
(print (list (foo))) is not well defined.
[change_end]
-------------------------------------------------------------------------------
29.3. Survey of Concepts
[change_begin]
This section discusses various aspects of the condition system by topic,
illustrating them with extensive examples. The next section contains
definitions of specific functions, macros, and other facilities.
[change_end]
-------------------------------------------------------------------------------
* Signaling Errors
* Trapping Errors
* Handling Conditions
* Object-Oriented Basis of Condition Handling
* Restarts
* Anonymous Restarts
* Named Restarts
* Restart Functions
* Comparison of Restarts and Catch/Throw
* Generalized Restarts
* Interactive Condition Handling
* Serious Conditions
* Non-Serious Conditions
* Condition Types
* Signaling Conditions
* Resignaling Conditions
* Condition Handlers
* Printing Conditions
-------------------------------------------------------------------------------
29.3.1. Signaling Errors
[change_begin]
Conceptually, signaling an error in a program is an admission by that program
that it does not know how to continue and requires external intervention. Once
an error is signaled, any decision about how to continue must come from the
``outside.''
The simplest way to signal an error is to use the error function with
format-style arguments describing the error for the sake of the user interface.
If error is called and there are no active handlers (described in sections
29.3.2 and 29.3.3), the debugger will be entered and the error message will be
typed out. For example:
Lisp> (defun factorial (x)
(cond ((or (not (typep x 'integer)) (minusp x))
(error "~S is not a valid argument to FACTORIAL."
x))
((zerop x) 1)
(t (* x (factorial (- x 1))))))
=> FACTORIAL
Lisp> (factorial 20)
=> 2432902008176640000
Lisp> (factorial -1)
Error: -1 is not a valid argument to FACTORIAL.
To continue, type :CONTINUE followed by an option number:
1: Return to Lisp Toplevel.
Debug>
In general, a call to error cannot directly return. Unless special work has
been done to override this behavior, the debugger will be entered and there
will be no option to simply continue.
The only exception may be that some implementations may provide debugger
commands for interactively returning from individual stack frames; even then,
however, such commands should never be used except by someone who has read the
erring code and understands the consequences of continuing from that point. In
particular, the programmer should feel confident about writing code like this:
(defun wargames:no-win-scenario ()
(when (true) (error "Pushing the button would be stupid."))
(push-the-button))
In this scenario, there should be no chance that the function error will return
and the button will be pushed.
-------------------------------------------------------------------------------
Remark: It should be noted that the notion of ``no chance'' that the button
will be pushed is relative only to the language model; it assumes that the
language is accurately implemented. In practice, compilers have bugs, computers
have glitches, and users have been known to interrupt at inopportune moments
and use the debugger to return from arbitrary stack frames. Such violations of
the language model are beyond the scope of the condition system but not
necessarily beyond the scope of potential failures that the programmer should
consider and defend against. The possibility of such unusual failures may of
course also influence the design of code meant to handle less drastic
situations, such as maintaining a database uncorrupted.-KMP and GLS
-------------------------------------------------------------------------------
In some cases, the programmer may have a single, well-defined idea of a
reasonable recovery strategy for this particular error. In that case, he can
use the function cerror, which specifies information about what would happen if
the user did simply continue from the call to cerror. For example:
Lisp> (defun factorial (x)
(cond ((not (typep x 'integer))
(error "~S is not a valid argument to FACTORIAL."
x))
((minusp x)
(let ((x-magnitude (- x)))
(cerror "Compute -(~D!) instead."
"(-~D)! is not defined." x-magnitude)
(- (factorial x-magnitude))))
((zerop x) 1)
(t (* x (factorial (- x 1))))))
=> FACTORIAL
Lisp> (factorial -3)
Error: (-3)! is not defined.
To continue, type :CONTINUE followed by an option number:
1: Compute -(3!) instead.
2: Return to Lisp Toplevel.
Debug> :continue 1
=> -6
[change_end]
-------------------------------------------------------------------------------
29.3.1. Signaling Errors
[change_begin]
Conceptually, signaling an error in a program is an admission by that program
that it does not know how to continue and requires external intervention. Once
an error is signaled, any decision about how to continue must come from the
``outside.''
The simplest way to signal an error is to use the error function with
format-style arguments describing the error for the sake of the user interface.
If error is called and there are no active handlers (described in sections
29.3.2 and 29.3.3), the debugger will be entered and the error message will be
typed out. For example:
Lisp> (defun factorial (x)
(cond ((or (not (typep x 'integer)) (minusp x))
(error "~S is not a valid argument to FACTORIAL."
x))
((zerop x) 1)
(t (* x (factorial (- x 1))))))
=> FACTORIAL
Lisp> (factorial 20)
=> 2432902008176640000
Lisp> (factorial -1)
Error: -1 is not a valid argument to FACTORIAL.
To continue, type :CONTINUE followed by an option number:
1: Return to Lisp Toplevel.
Debug>
In general, a call to error cannot directly return. Unless special work has
been done to override this behavior, the debugger will be entered and there
will be no option to simply continue.
The only exception may be that some implementations may provide debugger
commands for interactively returning from individual stack frames; even then,
however, such commands should never be used except by someone who has read the
erring code and understands the consequences of continuing from that point. In
particular, the programmer should feel confident about writing code like this:
(defun wargames:no-win-scenario ()
(when (true) (error "Pushing the button would be stupid."))
(push-the-button))
In this scenario, there should be no chance that the function error will return
and the button will be pushed.
-------------------------------------------------------------------------------
Remark: It should be noted that the notion of ``no chance'' that the button
will be pushed is relative only to the language model; it assumes that the
language is accurately implemented. In practice, compilers have bugs, computers
have glitches, and users have been known to interrupt at inopportune moments
and use the debugger to return from arbitrary stack frames. Such violations of
the language model are beyond the scope of the condition system but not
necessarily beyond the scope of potential failures that the programmer should
consider and defend against. The possibility of such unusual failures may of
course also influence the design of code meant to handle less drastic
situations, such as maintaining a database uncorrupted.-KMP and GLS
-------------------------------------------------------------------------------
In some cases, the programmer may have a single, well-defined idea of a
reasonable recovery strategy for this particular error. In that case, he can
use the function cerror, which specifies information about what would happen if
the user did simply continue from the call to cerror. For example:
Lisp> (defun factorial (x)
(cond ((not (typep x 'integer))
(error "~S is not a valid argument to FACTORIAL."
x))
((minusp x)
(let ((x-magnitude (- x)))
(cerror "Compute -(~D!) instead."
"(-~D)! is not defined." x-magnitude)
(- (factorial x-magnitude))))
((zerop x) 1)
(t (* x (factorial (- x 1))))))
=> FACTORIAL
Lisp> (factorial -3)
Error: (-3)! is not defined.
To continue, type :CONTINUE followed by an option number:
1: Compute -(3!) instead.
2: Return to Lisp Toplevel.
Debug> :continue 1
=> -6
[change_end]
-------------------------------------------------------------------------------
29.3.2. Trapping Errors
[change_begin]
By default, a call to error will force entry into the debugger. You can
override that behavior in a variety of ways. The simplest (and most blunt) tool
for inhibiting entry to the debugger on an error is to use ignore-errors. In
the normal situation, forms in the body of ignore-errors are evaluated
sequentially and the last value is returned. If a condition of type error is
signaled, ignore-errors immediately returns two values, namely nil and the
condition that was signaled; the debugger is not entered and no error message
is printed. For example:
Lisp> (setq filename "nosuchfile")
=> "nosuchfile"
Lisp> (ignore-errors (open filename :direction :input))
=> NIL and #<FILE-ERROR 3437523>
The second return value is an object that represents the kind of error. This is
explained in greater detail in section 29.3.4.
In many cases, however, ignore-errors is not desirable because it deals with
too many kinds of errors. Contrary to the belief of some, a program that does
not enter the debugger is not necessarily better than one that does. Excessive
use of ignore-errors may keep the program out of the debugger, but it may not
increase the program's reliability, because the program may continue to run
after encountering errors other than those you meant to work past. In general,
it is better to attempt to deal only with the particular kinds of errors that
you believe could legitimately happen. That way, if an unexpected error comes
along, you will still find out about it.
ignore-errors is a useful special case built from a more general facility,
handler-case, that allows the programmer to deal with particular kinds of
conditions (including non-error conditions) without affecting what happens when
other kinds of conditions are signaled. For example, an effect equivalent to
that of ignore-errors above is achieved in the following example:
Lisp> (setq filename "nosuchfile")
=> "nosuchfile"
Lisp> (handler-case (open filename :direction :input)
(error (condition)
(values nil condition)))
=> NIL and #<FILE-ERROR 3437525>
However, using handler-case, one can indicate a more specific condition type
than just ``error.'' Condition types are explained in detail later, but the
syntax looks roughly like the following:
Lisp> (makunbound 'filename)
=> FILENAME
Lisp> (handler-case (open filename :direction :input)
(file-error (condition)
(values nil condition)))
Error: The variable FILENAME is unbound.
To continue, type :CONTINUE followed by an option number:
1: Retry getting the value of FILENAME.
2: Specify a value of FILENAME to use this time.
3: Specify a value of FILENAME to store and use.
4: Return to Lisp Toplevel.
Debug>
[change_end]
-------------------------------------------------------------------------------
29.3.3. Handling Conditions
[change_begin]
Blind transfer of control to a handler-case is only one possible kind of
recovery action that can be taken when a condition is signaled. The low-level
mechanism offers great flexibility in how to continue once a condition has been
signaled.
The basic idea behind condition handling is that a piece of code called the
signaler recognizes and announces the existence of an exceptional situation
using signal or some function built on signal (such as error).
The process of signaling involves the search for and invocation of a handler, a
piece of code that will attempt to deal appropriately with the situation.
If a handler is found, it may either handle the situation, by performing some
non-local transfer of control, or decline to handle it, by failing to perform a
non-local transfer of control. If it declines, other handlers are sought.
Since the lexical environment of the signaler might not be available to
handlers, a data structure called a condition is created to represent
explicitly the relevant state of the situation. A condition either is created
explicitly using make-condition and then passed to a function such as signal,
or is created implicitly by a function such as signal when given appropriate
non-condition arguments.
In order to handle the error, a handler is permitted to use any non-local
transfer of control such as go to a tag in a tagbody, return from a block, or
throw to a catch. In addition, structured abstractions of these primitives are
provided for convenience in exception handling.
A handler can be made dynamically accessible to a program by use of
handler-bind. For example, to create a handler for a condition of type
arithmetic-error, one might write:
(handler-bind ((arithmetic-error handler))body)
The handler is a function of one argument, the condition. If a condition of the
designated type is signaled while the body is executing (and there are no
intervening handlers), the handler would be invoked on the given condition,
allowing it the option of transferring control. For example, one might write a
macro that executes a body, returning either its value(s) or the two values nil
and the condition:
(defmacro without-arithmetic-errors (&body forms)
(let ((tag (gensym)))
`(block ,tag
(handler-bind ((arithmetic-error
#'(lambda (c) ;Argument c is a condition
(return-from ,tag (values nil c)))))
,@body))))
The handler is executed in the dynamic context of the signaler, except that the
set of available condition handlers will have been rebound to the value that
was active at the time the condition handler was made active. If a handler
declines (that is, it does not transfer control), other handlers are sought. If
no handler is found and the condition was signaled by error or cerror (or some
function such as assert that behaves like these functions), the debugger is
entered, still in the dynamic context of the signaler.
[change_end]
-------------------------------------------------------------------------------
29.3.4. Object-Oriented Basis of Condition Handling
[change_begin]
Of course, the ability of the handler to usefully handle an exceptional
situation is related to the quality of the information it is provided. For
example, if all errors were signaled by
(error "some format string")
then the only piece of information that would be accessible to the handler
would be an object of type simple-error that had a slot containing the format
string.
If this were done, string-equal would be the preferred way to tell one error
from another, and it would be very hard to allow flexibility in the
presentation of error messages because existing handlers would tend to be
broken by even tiny variations in the wording of an error message. This
phenomenon has been the major failing of most error systems previously
available in Lisp. It is fundamentally important to decouple the error message
string (the human interface) from the objects that formally represent the error
state (the program interface). We therefore have the notion of typed
conditions, and of formal operations on those conditions that make them
inspectable in a structured way.
This object-oriented approach to condition handling has the following important
advantages over a text-based approach:
* Conditions are classified according to subtype relationships, making it
easy to test for categories of conditions.
* Conditions have named slot values through which parameters are conveyed
from the program that signals the condition to the program that handles
it.
* Inheritance of methods and slots reduces the amount of explicit
specification necessary to achieve various interesting effects.
Some condition types are defined by this document, but the set of condition
types is extensible using define-condition. Common Lisp condition types are in
fact CLOS classes, and condition objects are ordinary CLOS objects;
define-condition merely provides an abstract interface that is a bit more
convenient than defclass for defining conditions.
Here, as an example, we define a two-argument function called divide that is
patterned after the / function but does some stylized error checking:
(defun divide (numerator denominator)
(cond ((or (not (numberp numerator))
(not (numberp denominator)))
(error "(DIVIDE '~S '~S) - Bad arguments."
numerator denominator))
((zerop denominator)
(error 'division-by-zero
:operator 'divide
:operands (list numerator denominator)))
(t ...)))
Note that in the first clause we have used error with a string argument and in
the second clause we have named a particular condition type, division-by-zero.
In the case of a string argument, the condition type that will be signaled is
simple-error.
The particular kind of error that is signaled may be important in cases where
handlers are active. For example, simple-error inherits from type error, which
in turn inherits from type condition. On the other hand, division-by-zero
inherits from arithmetic-error, which inherits from error, which inherits from
condition. So if a handler existed for arithmetic-error while a
division-by-zero condition was signaled, that handler would be tried; however,
if a simple-error condition were signaled in the same context, the handler for
type arithmetic-error would not be tried.
[change_end]
-------------------------------------------------------------------------------
29.3.5. Restarts
[change_begin]
In older Lisp dialects (such as MacLisp), an attempt to signal an error of a
given type often carried with it an implicit promise to support the standard
recovery strategy for that type of error. If the signaler knew the type of
error but for whatever reason was unable to deal with the standard recovery
strategy for that kind of error, it was necessary to signal an untyped error
(for which there was no defined recovery strategy). This sometimes led to
confusion when people signaled typed errors without realizing the full
implications of having done so, but more often than not it meant that users
simply avoided typed errors altogether.
The Common Lisp Condition System, which is modeled after the Zetalisp condition
system, corrects this troublesome aspect of previous Lisp dialects by creating
a clear separation between the act of signaling an error of a particular type
and the act of saying that a particular way of recovery is appropriate. In the
divide example above, simply signaling an error does not imply a willingness on
the part of the signaler to cooperate in any corrective action. For example,
the following sample interaction illustrates that the only recovery action
offered for this error is ``Return to Lisp Toplevel'':
Lisp> (+ (divide 3 0) 7)
Error: Attempt to divide 3 by 0.
To continue, type :CONTINUE followed by an option number:
1: Return to Lisp Toplevel.
Debug> :continue 1
Returned to Lisp Toplevel.
Lisp>
When an error is detected and the function error is called, execution cannot
continue normally because error will not directly return. Control can be
transferred to other points in the program, however, by means of specially
established ``restarts.''
[change_end]
-------------------------------------------------------------------------------
29.3.6. Anonymous Restarts
[change_begin]
The simplest kind of restart involves structured transfer of control using a
macro called restart-case. The restart-case form allows execution of a piece of
code in a context where zero or more restarts are active, and where if one of
those restarts is ``invoked,'' control will be transferred to the corresponding
clause in the restart-case form. For example, we could rewrite the previous
divide example as follows.
(defun divide (numerator denominator)
(loop
(restart-case
(return
(cond ((or (not (numberp numerator))
(not (numberp denominator)))
(error "(DIVIDE '~S '~S) - Bad arguments."
numerator denominator))
((zerop denominator)
(error 'division-by-zero
:operator 'divide
:operands (list numerator denominator)))
(t ...)))
(nil (arg1 arg2)
:report "Provide new arguments for use by DIVIDE."
:interactive
(lambda ()
(list (prompt-for 'number "Numerator: ")
(prompt-for 'number "Denominator: ")))
(setq numerator arg1 denominator arg2))
(nil (result)
:report "Provide a value to return from DIVIDE."
:interactive
(lambda () (list (prompt-for 'number "Result: ")))
(return result)))))
-------------------------------------------------------------------------------
Remark: The function prompt-for used in this chapter in a number of places is
not a part of Common Lisp. It is used in the examples in this chapter only to
keep the presentation simple. It is assumed to accept a type specifier and
optionally a format string and associated arguments. It uses the format string
and associated arguments as part of an interactive prompt, and uses read to
read a Lisp object; however, only an object of the type indicated by the type
specifier is accepted.
The question of whether or not prompt-for (or something like it) would be a
useful addition to Common Lisp is under consideration by X3J13, but as of
January 1989 no action has been taken. In spite of its use in a number of
examples, nothing in the Common Lisp Condition System depends on this function.
-------------------------------------------------------------------------------
In the example, the nil at the head of each clause means that it is an
``anonymous'' restart. Anonymous restarts are typically invoked only from
within the debugger. As we shall see later, it is possible to have ``named
restarts'' that may be invoked from code without the need for user
intervention.
If the arguments to anonymous restarts are not optional, then special
information must be provided about what the debugger should use as arguments.
Here the :interactive keyword is used to specify that information.
The :report keyword introduces information to be used when presenting the
restart option to the user (by the debugger, for example).
Here is a sample interaction that takes advantage of the restarts provided by
the revised definition of divide:
Lisp> (+ (divide 3 0) 7)
Error: Attempt to divide 3 by 0.
To continue, type :CONTINUE followed by an option number:
1: Provide new arguments for use by the DIVIDE function.
2: Provide a value to return from the DIVIDE function.
3: Return to Lisp Toplevel.
Debug> :continue 1
Numerator: 4
Denominator: 2
=> 9
[change_end]
-------------------------------------------------------------------------------
29.3.7. Named Restarts
[change_begin]
In addition to anonymous restarts, one can have named restarts, which can be
invoked by name from within code. As a trivial example, one could write
(restart-case (invoke-restart 'foo 3)
(foo (x) (+ x 1)))
to add 3 to 1, returning 4. This trivial example is conceptually analogous to
writing:
(+ (catch 'something (throw 'something 3)) 1)
For a more realistic example, the code for the function symbol-value might
signal an unbound variable error as follows:
(restart-case (error "The variable ~S is unbound." variable)
(continue ()
:report
(lambda (s) ;Argument s is a stream
(format s "Retry getting the value of ~S." variable))
(symbol-value variable))
(use-value (value)
:report
(lambda (s) ;Argument s is a stream
(format s "Specify a value of ~S to use this time."
variable))
value)
(store-value (value)
:report
(lambda (s) ;Argument s is a stream
(format s "Specify a value of ~S to store and use."
variable))
(setf (symbol-value variable) value)
value))
If this were part of the implementation of symbol-value, then it would be
possible for users to write a variety of automatic handlers for unbound
variable errors. For example, to make unbound variables evaluate to themselves,
one might write
(handler-bind ((unbound-variable
#'(lambda (c) ;Argument c is a condition
(when (find-restart 'use-value)
(invoke-restart 'use-value
(cell-error-name c))))))
body)
[change_end]
-------------------------------------------------------------------------------
29.3.8. Restart Functions
[change_begin]
For commonly used restarts, it is conventional to define a program interface
that hides the use of invoke-restart. Such program interfaces to restarts are
called restart functions.
The normal convention is for the function to share the name of the restart. The
pre-defined functions abort, continue, muffle-warning, store-value, and
use-value are restart functions. With use-value the above example of
handler-bind could have been written more concisely as
(handler-bind ((unbound-variable
#'(lambda (c) ;Argument c is a condition
(use-value (cell-error-name c)))))
body)
[change_end]
-------------------------------------------------------------------------------
29.3.9. Comparison of Restarts and Catch/Throw
[change_begin]
One important feature that restart-case (or restart-bind) offers that catch
does not is the ability to reason about the available points to which control
might be transferred without actually attempting the transfer. One could, for
example, write
(ignore-errors (throw ...))
which is a sort of poor man's variation of
(when (find-restart 'something)
(invoke-restart 'something))
but there is no way to use ignore-errors and throw to simulate something like
(when (and (find-restart 'something)
(find-restart 'something-else))
(invoke-restart 'something))
or even just
(when (and (find-restart 'something)
(yes-or-no-p "Do something? "))
(invoke-restart 'something))
because the degree of inspectability that comes with simply writing
(ignore-errors (throw ...))
is too primitive-getting the desired information also forces transfer of
control, perhaps at a time when it is not desirable.
Many programmers have previously evolved strategies like the following on a
case-by-case basis:
(defvar *foo-tag-is-available* nil)
(defun fn-1 ()
(catch 'foo
(let ((*foo-tag-is-available* t))
... (fn-2) ...)))
(defun fn-2 ()
...
(if *foo-tag-is-available* (throw 'foo t))
...)
The facility provided by restart-case and find-restart is intended to provide a
standardized protocol for this sort of information to be communicated between
programs that were developed independently so that individual variations from
program to program do not thwart the overall modularity and debuggability of
programs.
Another difference between the restart facility and the catch/throw facility is
that a catch with any given tag completely shadows any outer pending catch that
uses the same tag. Because of the presence of compute-restarts, however, it is
possible to see shadowed restarts, which may be very useful in some situations
(particularly in an interactive debugger).
[change_end]
-------------------------------------------------------------------------------
29.3.10. Generalized Restarts
[change_begin]
restart-case is a mechanism that allows only imperative transfer of control for
its associated restarts. restart-case is built on a lower-level mechanism
called restart-bind, which does not force transfer of control.
restart-bind is to restart-case as handler-bind is to handler-case. The syntax
is
(restart-bind ((name function . options)) . body)
The body is executed in a dynamic context within which the function will be
called whenever (invoke-restart 'name) is executed. The options are
keyword-style and are used to pass information such as that provided with the
:report keyword in restart-case.
A restart-case expands into a call to restart-bind where the function simply
does an unconditional transfer of control to a particular body of code, passing
along ``argument'' information in a structured way.
It is also possible to write restarts that do not transfer control. Such
restarts may be useful in implementing various special commands for the
debugger that are of interest only in certain situations. For example, one
might imagine a situation where file space was exhausted and the following was
done in an attempt to free space in directory dir:
(restart-bind ((nil #'(lambda () (expunge-directory dir))
:report-function
#'(lambda (stream)
(format stream "Expunge ~A."
(directory-namestring dir)))))
(cerror "Try this file operation again."
'directory-full :directory dir))
In this case, the debugger might be entered and the user could first perform
the expunge (which would not transfer control from the debugger context) and
then retry the file operation:
Lisp> (open "FOO" :direction :output)
Error: The directory PS:<JDOE> is full.
To continue, type :CONTINUE followed by an option number:
1: Try this file operation again.
2: Expunge PS:<JDOE>.
3: Return to Lisp Toplevel.
Debug> :continue 2
Expunging PS:<JDOE> ... 3 records freed.
Debug> :continue 1
=> #<OUTPUT-STREAM "PS:<JDOE>FOO.LSP" 2323473>
[change_end]
-------------------------------------------------------------------------------
29.3.11. Interactive Condition Handling
[change_begin]
When a program does not know how to continue, and no active handler is able to
advise it, the ``interactive condition handler,'' or ``debugger,'' can be
entered. This happens implicitly through the use of functions such as error and
cerror, or explicitly through the use of the function invoke-debugger.
The interactive condition handler never returns directly; it returns only
through structured non-local transfer of control to specially defined restart
points that can be set up either by the system or by user code. The mechanisms
that support the establishment of such structured restart points for portable
code are outlined in sections 29.3.5 through 29.3.10.
Actually, implementations may also provide extended debugging facilities that
allow return from arbitrary stack frames. Although such commands are frequently
useful in practice, their effects are implementation-dependent because they
violate the Common Lisp program abstraction. The effect of using such commands
is undefined with respect to Common Lisp.
[change_end]
-------------------------------------------------------------------------------
29.3.12. Serious Conditions
[change_begin]
The ignore-errors macro will trap conditions of type error. There are, however,
conditions that are not of type error.
Some conditions are not considered errors but are still very serious, so we
call them serious conditions and we use the type serious-condition to represent
them. Conditions such as those that might be signaled for ``stack overflow'' or
``storage exhausted'' are in this category.
The type error is a subtype of serious-condition, and it would technically be
correct to use the term ``serious condition'' to refer to all serious
conditions whether errors or not. However, normally we use the term ``serious
condition'' to refer to things of type serious-condition but not of type error.
The point of the distinction between errors and other serious conditions is
that some conditions are known to occur for reasons that are beyond the scope
of Common Lisp to specify clearly. For example, we know that a stack will
generally be used to implement function calling, and we know that stacks tend
to be of finite size and are prone to overflow. Since the available stack size
may vary from implementation to implementation, from session to session, or
from function call to function call, it would be confusing to have expressions
such as (ignore-errors (+ a b)) return a number sometimes and nil other times
if a and b were always bound to numbers and the stack just happened to overflow
on a particular call. For this reason, only conditions of type error and not
all conditions of type serious-condition are trapped by ignore-errors. To trap
other conditions, a lower-level facility must be used (such as handler-bind or
handler-case).
By convention, the function error is preferred over signal to signal conditions
of type serious-condition (including those of type error). It is the use of the
function error, and not the type of the condition being signaled, that actually
causes the debugger to be entered.
-------------------------------------------------------------------------------
Compatibility note: The Common Lisp Condition System differs from that of
Zetalisp in this respect. In Zetalisp the debugger is entered for an unhandled
signal if the error function is used or if the condition is of type error.
-------------------------------------------------------------------------------
[change_end]
-------------------------------------------------------------------------------
29.3.13. Non-Serious Conditions
[change_begin]
Some conditions are neither errors nor serious conditions. They are signaled to
give other programs a chance to intervene, but if no action is taken,
computation simply continues normally.
For example, an implementation might choose to signal a non-serious (and
implementation-dependent) condition called end-of-line when output reaches the
last character position on a line of character output. In such an
implementation, the signaling of this condition might allow a convenient way
for other programs to intervene, producing output that is truncated at the end
of a line.
By convention, the function signal is used to signal conditions that are not
serious. It would be possible to signal serious conditions using signal, and
the debugger would not be entered if the condition went unhandled. However, by
convention, handlers will generally tend to assume that serious conditions and
errors were signaled by calling the error function (and will therefore force
entry to the interactive condition handler) and that they should work to avoid
this.
[change_end]
-------------------------------------------------------------------------------
29.3.14. Condition Types
[change_begin]
Some types of conditions are predefined by the system. All types of conditions
are subtypes of condition. That is, (typep x 'condition) is true if and only if
the value of x is a condition.
Implementations supporting multiple (or non-hierarchical) type inheritance are
expressly permitted to exploit multiple inheritance in the tree of condition
types as implementation-dependent extensions, as long as such extensions are
compatible with the specifications in this chapter. [X3J13 voted in March 1989
(ZLOS-CONDITIONS) to integrate the Condition System and the Object System, so
multiple inheritance is always available for condition types.-GLS]
In order to avoid problems in portable code that runs both in systems with
multiple type inheritance and in systems without it, programmers are explicitly
warned that while all correct Common Lisp implementations will ensure that
(typep c 'condition) is true for all conditions c (and all subtype
relationships indicated in this chapter will also be true), it should not be
assumed that two condition types specified to be subtypes of the same third
type are disjoint. (In some cases, disjoint subtypes are identified explicitly,
but such disjointness is not to be assumed by default.) For example, it follows
from the subtype descriptions contained in this chapter that in all
implementations (typep c 'control-error) implies (typep c 'error), but note
that (typep c 'control-error) does not imply (not (typep c 'cell-error)).
[change_end]
-------------------------------------------------------------------------------
29.3.15. Signaling Conditions
[change_begin]
When a condition is signaled, the system tries to locate the most appropriate
handler for the condition and to invoke that handler.
Handlers are established dynamically using handler-bind or abstractions built
on handler-bind.
If an appropriate handler is found, it is called. In some circumstances, the
handler may decline simply by returning without performing a non-local transfer
of control. In such cases, the search for an appropriate handler is picked up
where it left off, as if the called handler had never been present.
If no handler is found, or if all handlers that were found decline, signal
returns nil.
Although it follows from the description above, it is perhaps worth noting
explicitly that the lookup procedure described here will prefer a general but
more (dynamically) local handler over a specific but less (dynamically) local
handler. Experience with existing condition systems suggests that this is a
reasonable approach and works adequately in most situations. Some care should
be taken when binding handlers for very general kinds of conditions, such as is
done in ignore-errors. Often, binding for a more specific condition type than
error is more appropriate.
[change_end]
-------------------------------------------------------------------------------
29.3.16. Resignaling Conditions
[change_begin]
[The contents of this section are still a subject of some debate within X3J13.
The reader may wish to take this section with a grain of salt.-GLS]
Note that signaling a condition has no side effect on that condition, and that
there is no dynamic state contained in a condition object. As such, it may at
times be reasonable and appropriate to consider caching condition objects for
repeated use, re-signaling conditions from within handlers, or saving
conditions away somewhere and re-signaling them later.
For example, it may be desirable for the system to pre-allocate objects of type
storage-condition so that they can be signaled when needed without attempting
to allocate more storage.
[change_end]
-------------------------------------------------------------------------------
29.3.17. Condition Handlers
[change_begin]
A handler is a function of one argument, the condition to be handled. The
handler may inspect the object to be sure it is ``interested'' in handling the
condition.
A handler is executed in the dynamic context of the signaler, except that the
set of available condition handlers will have been rebound to the value that
was active at the time the condition handler was made active. The intent of
this is to prevent infinite recursion because of errors in a condition handler.
After inspecting the condition, the handler should take one of the following
actions:
* It might decline to handle the condition (by simply returning). When this
happens, the returned values are ignored and the effect is the same as if
the handler had been invisible to the mechanism seeking to find a handler.
The next handler in line will be tried, or if no such handler exists, the
condition will go unhandled.
* It might handle the condition (by performing some non-local transfer of
control). This may be done either primitively using go, return, or throw,
or more abstractly using a function such as abort or invoke-restart.
* It might signal another condition.
* It might invoke the interactive debugger.
In fact, the latter two actions (signaling another condition or entering the
debugger) are really just ways of putting off the decision to either handle or
decline, or trying to get someone else to make such a decision. Ultimately, all
a handler can do is to handle or decline to handle.
[change_end]
-------------------------------------------------------------------------------
29.3.18. Printing Conditions
[change_begin]
When *print-escape* is nil (for example, when the princ function or the ~A
directive is used with format), the report method for the condition will be
invoked. This will be done automatically by functions such as invoke-debugger,
break, and warn, but there may still be situations in which it is desirable to
have a condition report under explicit user control. For example,
(let ((form '(open "nosuchfile")))
(handler-case (eval form)
(serious-condition (c)
(format t "~&Evaluation of ~S failed:~%~A" form c))))
might print something like
Evaluation of (OPEN "nosuchfile") failed:
The file "nosuchfile" was not found.
Some suggestions about the form of text typed by report methods:
* The message should generally be a complete sentence, beginning with a
capital letter and ending with appropriate punctuation (usually a period).
* The message should not include any introductory text such as ``Error:''
or ``Warning:'' and should not be followed by a trailing newline. Such
text will be added as may be appropriate to context by the routine
invoking the report method.
* Except where unavoidable, the tab character (which is only semi-standard
anyway) should not be used in error messages. Its effect may vary from one
implementation to another and may cause problems even within an
implementation because it may do different things depending on the column
at which the error report begins.
* Single-line messages are preferred, but newlines in the middle of long
messages are acceptable.
* If any program (for example, the debugger) displays messages indented
from the prevailing left margin (for example, indented seven spaces
because they are prefixed by the seven-character herald ``Error: ''), then
that program will take care of inserting the appropriate indentation into
the extra lines of a multi-line error message. Similarly, a program that
prefixes error messages with semicolons so that they appear to be comments
should take care of inserting a semicolon at the beginning of each line in
a multi-line error message. (These rules are important because, even
within a single implementation, there may be more than one program that
presents error messages to the user, and they may use different styles of
presentation. The caller of error cannot anticipate all such possible
styles, and so it is incumbent upon the presenter of the message to make
any necessary adjustments.)
[Note: These recommendations expand upon those in section 24.1.-GLS]
When *print-escape* is not nil, the object should print in some useful (but
usually fairly abbreviated) fashion according to the style of the
implementation. It is not expected that a condition will be printed in a form
suitable for read. Something like #<ARITHMETIC-ERROR 1734> is fine.
X3J13 voted in March 1989 (ZLOS-CONDITIONS) to integrate the Condition System
and the Object System. In the original Condition System proposal, no function
was provided for directly accessing or setting the printer for a condition
type, or for invoking it; the techniques described above were the sole
interface to reporting. The vote specified that, in CLOS terms, condition
reporting is mediated through the print-object method for the condition type
(that is, class) in question, with *print-escape* bound to nil. Specifying
(:report fn) to define-condition when defining condition type C is equivalent
to a separate method definition:
(defmethod print-object ((x C) stream)
(if *print-escape*
(call-next-method)
(funcall #'fn x stream)))
Note that the method uses fn to print the condition only when *print-escape*
has the value nil.
[change_end]
-------------------------------------------------------------------------------
29.4. Program Interface to the Condition System
[change_begin]
This section describes functions, macros, variables, and condition types
associated with the Common Lisp Condition System.
[change_end]
-------------------------------------------------------------------------------
* Signaling Conditions
* Assertions
* Exhaustive Case Analysis
* Handling Conditions
* Defining Conditions
* Creating Conditions
* Establishing Restarts
* Finding and Manipulating Restarts
* Warnings
* Restart Functions
* Debugging Utilities
-------------------------------------------------------------------------------
29.4.1. Signaling Conditions
[change_begin]
The functions in this section provide various mechanisms for signaling
warnings, breaks, continuable errors, and fatal errors.
[Function]
error datum &rest arguments
[This supersedes the description of error given in section 24.1.-GLS]
Invokes the signal facility on a condition. If the condition is not handled,
(invoke-debugger condition) is executed. As a consequence of calling
invoke-debugger, error never directly returns to its caller; the only exit from
this function can come by non-local transfer of control in a handler or by use
of an interactive debugging command.
If datum is a condition, then that condition is used directly. In this case, it
is an error for the list of arguments to be non-empty; that is, error must have
been called with exactly one argument, the condition.
If datum is a condition type (a class or class name), then the condition used
is effectively the result of (apply #'make-condition datum arguments).
If datum is a string, then the condition used is effectively the result of
(make-condition 'simple-error
:format-string datum
:format-arguments arguments)
[Function]
cerror continue-format-string datum &rest arguments
[This supersedes the description of cerror given in section 24.1.-GLS]
The function cerror invokes the error facility on a condition. If the condition
is not handled, (invoke-debugger condition) is executed. While signaling is
going on, and while control is in the debugger (if it is reached), it is
possible to continue program execution (thereby returning from the call to
cerror) using the continue restart.
If datum is a condition, then that condition is used directly. In this case,
the list of arguments need not be empty, but will be used only with the
continue-format-string and will not be used to initialize datum.
If datum is a condition type (a class or class name), then the condition used
is effectively the result of (apply #'make-condition datum arguments).
If datum is a string, then the condition used is effectively the result of
(make-condition 'simple-error
:format-string datum
:format-arguments arguments)
The continue-format-string must be a string. Note that if datum is not a
string, then the format arguments used by the continue-format-string will still
be the list of arguments (which is in keyword format if datum is a condition
type). In this case, some care may be necessary to set up the
continue-format-string correctly. The format directive ~*, which ignores and
skips over format arguments, may be particularly useful in this situation.
The value returned by cerror is nil.
[Function]
signal datum &rest arguments
Invokes the signal facility on a condition. If the condition is not handled,
signal returns nil.
If datum is a condition, then that condition is used directly. In this case, it
is an error for the list of arguments to be non-empty; that is, error must have
been called with exactly one argument, the condition.
If datum is a condition type (a class or class name), then the condition used
is effectively the result of (apply #'make-condition datum arguments).
If datum is a string, then the condition used is effectively the result of
(make-condition 'simple-error
:format-string datum
:format-arguments arguments)
Note that if (typep condition *break-on-signals*) is true, then the debugger
will be entered prior to beginning the process of signaling. The continue
restart function may be used to continue with the signaling process; the
restart is associated with the signaled condition as if by use of
with-condition-restarts. This is true also for all other functions and macros
that signal conditions, such as warn, error, cerror, assert, and check-type.
During the dynamic extent of a call to signal with a particular condition, the
effect of calling signal again on that condition object for a distinct abstract
event is not defined. For example, although a handler may resignal a condition
in order to allow outer handlers first shot at handling the condition, two
distinct asynchronous keyboard events must not signal an the same (eq)
condition object at the same time.
For further details about signaling and handling, see the discussion of
condition handlers in section 29.3.17.
[Variable]
*break-on-signals*
This variable is intended primarily for use when the user is debugging programs
that do signaling. The value of *break-on-signals* should be suitable as a
second argument to typep, that is, a type or type specifier.
When (typep condition *break-on-signals*) is true, then calls to signal (and to
other advertised functions such as error that implicitly call signal) will
enter the debugger prior to signaling that condition. The continue restart may
be used to continue with the normal signaling process; the restart is
associated with the signaled condition as if by use of with-condition-restarts.
Note that nil is a valid type specifier. If the value of *break-on-signals* is
nil, then signal will never enter the debugger in this implicit manner.
When setting this variable, the user is encouraged to choose the most
restrictive specification that suffices. Setting this flag effectively violates
the modular handling of condition signaling that this chapter seeks to
establish. Its complete effect may be unpredictable in some cases, since the
user may not be aware of the variety or number of calls to signal that are used
in programs called only incidentally.
By default-and certainly in any ``production'' use-the value of this variable
should be nil, both for reasons of performance and for reasons of modularity
and abstraction.
X3J13 voted in March 1989 (BREAK-ON-WARNINGS-OBSOLETE) to remove
*break-on-warnings* from the language; *break-on-signals* offers all the power
of *break-on-warnings* and more.
-------------------------------------------------------------------------------
Compatibility note: This variable is similar to the Zetalisp variable
trace-conditions except for the obvious difference that zl:trace-conditions
takes a type or list of types while *break-on-signals* takes a single type
specifier.
[There is no loss of generality in Common Lisp because the or type specifier
may be used to indicate that any of a set of conditions should enter the
debugger.-GLS]
-------------------------------------------------------------------------------
[change_end]
-------------------------------------------------------------------------------
29.4.2. Assertions
[change_begin]
These facilities are designed to make it convenient for the user to insert
error checks into code.
[Macro]
check-type place typespec [string]
[This supersedes the description of check-type given in section 24.2.-GLS]
A check-type form signals an error of type type-error if the contents of place
are not of the desired type.
If a condition is signaled, handlers of this condition can use the functions
type-error-datum and type-error-expected-type to access the contents of place
and the typespec, respectively.
This function can return only if the store-value restart is invoked, either
explicitly from a handler or implicitly as one of the options offered by the
debugger. The restart is associated with the signaled condition as if by use of
with-condition-restarts.
If store-value is called, check-type will store the new value that is the
argument to store-value (or that is prompted for interactively by the debugger)
in place and start over, checking the type of the new value and signaling
another error if it is still not the desired type. Subforms of place may be
evaluated multiple times because of the implicit loop generated. check-type
returns nil.
The place must be a generalized variable reference acceptable to setf. The
typespec must be a type specifier; it is not evaluated. The string should be an
English description of the type, starting with an indefinite article (``a'' or
``an''); it is evaluated. If the string is not supplied, it is computed
automatically from the typespec. (The optional string argument is allowed
because some applications of check-type may require a more specific description
of what is wanted than can be generated automatically from the type specifier.)
The error message will mention the place, its contents, and the desired type.
-------------------------------------------------------------------------------
Implementation note: An implementation may choose to generate a somewhat
differently worded error message if it recognizes that place is of a particular
form, such as one of the arguments to the function that called check-type.
-------------------------------------------------------------------------------
Lisp> (setq aardvarks '(sam harry fred))
=> (SAM HARRY FRED)
Lisp> (check-type aardvarks (array * (3)))
Error: The value of AARDVARKS, (SAM HARRY FRED),
is not a 3-long array.
To continue, type :CONTINUE followed by an option number:
1: Specify a value to use instead.
2: Return to Lisp Toplevel.
Debug> :continue 1
Use Value: #(sam fred harry)
=> NIL
Lisp> aardvarks
=> #<ARRAY-3 13571>
Lisp> (map 'list #'identity aardvarks)
=> (SAM FRED HARRY)
Lisp> (setq aacount 'foo)
=> FOO
Lisp> (check-type aacount (integer 0 *) "a non-negative integer")
Error: The value of AACOUNT, FOO, is not a non-negative integer.
To continue, type :CONTINUE followed by an option number:
1: Specify a value to use instead.
2: Return to Lisp Toplevel.
Debug> :continue 2
Lisp>
-------------------------------------------------------------------------------
Compatibility note: In Zetalisp, the equivalent facility is called
check-arg-type.
-------------------------------------------------------------------------------
[Macro]
assert test-form [({place}*) [datum {argument}*]]
[This supersedes the description of assert given in section 24.2.-GLS]
An assert form signals an error if the value of the test-form is nil.
Continuing from this error using the continue restart will allow the user to
alter the values of some variables, and assert will then start over, evaluating
the test-form again. (The restart is associated with the signaled condition as
if by use of with-condition-restarts.) assert returns nil.
The test-form may be any form. Each place (there may be any number of them, or
none) must be a generalized variable reference acceptable to setf. These should
be variables on which test-form depends, whose values may sensibly be changed
by the user in attempting to correct the error. Subforms of each place are
evaluated only if an error is signaled, and may be re-evaluated if the error is
re-signaled (after continuing without actually fixing the problem).
The datum and arguments are evaluated only if an error is to be signaled, and
re-evaluated if the error is to be signaled again.
If datum is a condition, then that condition is used directly. In this case, it
is an error to specify any arguments.
If datum is a condition type (a class or class name), then the condition used
is effectively the result of (apply #'make-condition datum (list argument)).
If datum is a string, then the condition used is effectively the result of
(make-condition 'simple-error
:format-string datum
:format-arguments (list argument))
If datum is omitted, then a condition of type simple-error is constructed using
the test-form as data. For example, the following might be used:
(make-condition 'simple-error
:format-string "The assertion ~S failed."
:format-arguments '(test-form))
Note that the test-form itself, and not its value, is used as the format
argument.
-------------------------------------------------------------------------------
Implementation note: The debugger need not include the test-form in the error
message, and any places should not be included in the message, but they should
be made available for the user's perusal. If the user gives the ``continue''
command, an opportunity should be presented to alter the values of any or all
of the references. The details of this depend on the implementation's style of
user interface, of course.
-------------------------------------------------------------------------------
Here is an example of the use of assert:
(setq x (make-array '(3 5) :initial-element 3))
(setq y (make-array '(3 5) :initial-element 7))
(defun matrix-multiply (a b)
(let ((*print-array* nil))
(assert (and (= (array-rank a) (array-rank b) 2)
(= (array-dimension a 1)
(array-dimension b 0)))
(a b)
"Cannot multiply ~S by ~S." a b)
(really-matrix-multiply a b)))
(matrix-multiply x y)
Error: Cannot multiply #<ARRAY-3-5 12345> by #<ARRAY-3-5 12364>.
To continue, type :CONTINUE followed by an option number:
1: Specify new values.
2: Return to Lisp Toplevel.
Debug> :continue 1
Value for A: x
Value for B: (make-array '(5 3) :initial-element 6)
=> #2A(Ø(54 54 54 54 54)
(54 54 54 54 54)
(54 54 54 54 54)
(54 54 54 54 54)
(54 54 54 54 54))
[change_end]
-------------------------------------------------------------------------------
29.4.3. Exhaustive Case Analysis
[change_begin]
The syntax for etypecase and ctypecase is the same as for typecase, except that
no otherwise clause is permitted. Similarly, the syntax for ecase and ccase is
the same as for case except for the otherwise clause.
etypecase and ecase are similar to typecase and case, respectively, but signal
a non-continuable error rather than returning nil if no clause is selected.
ctypecase and ccase are also similar to typecase and case, respectively, but
signal a continuable error if no clause is selected.
[Macro]
etypecase keyform {(type {form}*)}*
[This supersedes the description of etypecase given in section 24.3.-GLS]
This control construct is similar to typecase, but no explicit otherwise or t
clause is permitted. If no clause is satisfied, etypecase signals an error (of
type type-error) with a message constructed from the clauses. It is not
permissible to continue from this error. To supply an error message, the user
should use typecase with an otherwise clause containing a call to error. The
name of this function stands for ``exhaustive type case'' or ``error-checking
type case.''
Example:
Lisp> (setq x 1/3)
=> 1/3
Lisp> (etypecase x
(integer (* x 4))
(symbol (symbol-value x)))
Error: The value of X, 1/3, is neither an integer nor a symbol.
To continue, type :CONTINUE followed by an option number:
1: Return to Lisp Toplevel.
Debug>
[Macro]
ctypecase keyplace {(type {form}*)}*
[This supersedes the description of ctypecase given in section 24.3.-GLS]
This control construct is similar to typecase, but no explicit otherwise or t
clause is permitted.
The keyplace must be a generalized variable reference acceptable to setf. If no
clause is satisfied, ctypecase signals an error (of type type-error) with a
message constructed from the clauses. This error may be continued using the
store-value restart. The argument to store-value is stored in keyplace and then
ctypecase starts over, making the type tests again. Subforms of keyplace may be
evaluated multiple times. If the store-value restart is invoked interactively,
the user will be prompted for the value to be used.
The name of this function is mnemonic for ``continuable (exhaustive) type
case.''
Example:
Lisp> (setq x 1/3)
=> 1/3
Lisp> (ctypecase x
(integer (* x 4))
(symbol (symbol-value x)))
Error: The value of X, 1/3, is neither an integer nor a symbol.
To continue, type :CONTINUE followed by an option number:
1: Specify a value to use instead.
2: Return to Lisp Toplevel.
Debug> :continue 1
Use value: 3.7
Error: The value of X, 3.7, is neither an integer nor a symbol.
To continue, type :CONTINUE followed by an option number:
1: Specify a value to use instead.
2: Return to Lisp Toplevel.
Debug> :continue 1
Use value: 12
=> 48
[Macro]
ecase keyform {({({key}*) | key} {form}*)}*
[This supersedes the description of ecase given in section 24.3.-GLS]
This control construct is similar to case, but no explicit otherwise or t
clause is permitted. If no clause is satisfied, ecase signals an error (of type
type-error) with a message constructed from the clauses. It is not permissible
to continue from this error. To supply an error message, the user should use
case with an otherwise clause containing a call to error. The name of this
function stands for ``exhaustive case'' or ``error-checking case.''
Example:
Lisp> (setq x 1/3)
=> 1/3
Lisp> (ecase x
(alpha (foo))
(omega (bar))
((zeta phi) (baz)))
Error: The value of X, 1/3, is not ALPHA, OMEGA, ZETA, or PHI.
To continue, type :CONTINUE followed by an option number:
1: Return to Lisp Toplevel.
Debug>
[Macro]
ccase keyplace {({({key}*) | key} {form}*)}*
[This supersedes the description of ccase given in section 24.3.-GLS]
This control construct is similar to case, but no explicit otherwise or t
clause is permitted.
The keyplace must be a generalized variable reference acceptable to setf. If no
clause is satisfied, ccase signals an error (of type type-error) with a message
constructed from the clauses. This error may be continued using the store-value
restart. The argument to store-value is stored in keyplace and then ccase
starts over, making the type tests again. Subforms of keyplace may be evaluated
multiple times. If the store-value restart is invoked interactively, the user
will be prompted for the value to be used.
The name of this function is mnemonic for ``continuable (exhaustive) case.''
-------------------------------------------------------------------------------
Implementation note: The type-error signaled by ccase and ecase is free to
choose any representation of the acceptable argument type that it wishes for
placement in the expected-type slot. It will always work to use type (member .
keys), but in some cases it may be more efficient, for example, to use a type
that represents an integer subrange or a type composed using the or type
specifier.
-------------------------------------------------------------------------------
[change_end]
-------------------------------------------------------------------------------
29.4.4. Handling Conditions
[change_begin]
These macros allow a program to gain control when a condition is signaled.
[Macro]
handler-case expression {(typespec ([var]) {form}*)}*
Executes the given expression in a context where various specified handlers are
active.
Each typespec may be any type specifier. If during the execution of the
expression a condition is signaled for which there is an appropriate
clause-that is, one for which (typep condition 'typespec) is true-and if there
is no intervening handler for conditions of that type, then control is
transferred to the body of the relevant clause (unwinding the dynamic state
appropriately in the process) and the given variable var is bound to the
condition that was signaled. If no such condition is signaled and the
computation runs to completion, then the values resulting from the expression
are returned by the handler-case form.
If more than one case is provided, those cases are made accessible in parallel.
That is, in
(handler-case expression
(type1 (var1) form1)
(type2 (var2) form2))
if the first clause (containing form1) has been selected, the handler for the
second is no longer visible (and vice versa).
The cases are searched sequentially from top to bottom. If a signaled condition
matches more than one case (possible if there is type overlap) the earlier of
the two cases will be selected.
If the variable var is not needed, it may be omitted. That is, a clause such as
(type (var) (declare (ignore var)) form)
may be written using the following shorthand notation:
(type () form)
If there are no forms in a selected case, the case returns nil. Note that
(handler-case expression
(type1 (var1) . body1)
(type2 (var2) . body2)
...)
is approximately equivalent to
(block #1=#:block-1
(let (#2=#:var-2)
(tagbody
(handler-bind ((type1 Ø#'(lambda (temp)
(setq #2# temp)
(go #3=#:tag-3)))
(type2 Ø#'(lambda (temp)
(setq #2# temp)
(go #4=#:tag-4)))
...)
(return-from #1# expression))
#3# (return-from #1# (let ((var1 #2#)) . body1))
#4# (return-from #1# (let ((var2 #2#)) . body2))
...)))
[Note the use of ``gensyms'' such as #:block-1 as block names, variables, and
tagbody tags in this example, and the use of #n= and #n# read-macro syntax to
indicate that the very same gensym appears in multiple places.-GLS]
As a special case, the typespec can also be the symbol :no-error in the last
clause. If it is, it designates a clause that will take control if the
expression returns normally. In that case, a completely general lambda-list may
follow the symbol :no-error, and the arguments to which the lambda-list
parameters are bound are like those for multiple-value-call on the return value
of the expression. For example,
(handler-case expression
(type1 (var1) . body1)
(type2 (var2) . body2)
...
(typen (varn) . bodyn)
(:no-error (nvar1 nvar2 ... nvarm) . nbody))
is approximately equivalent to
(block #1=#:error-return
(multiple-value-call #'(lambda (nvar1 nvar2 ... nvarm) . nbody)
(block #2=#:normal-return
(return-from #1#
(handler-case (return-from #2# expression)
(type1 (var1) . body1)
(type2 (var2) . body2)
...
(typen (varn) . bodyn))))))
Examples of the use of handler-case:
(handler-case (/ x y)
(division-by-zero () nil))
(handler-case (open *the-file* :direction :input)
(file-error (condition) (format t "~&Fooey: ~A~%" condition)))
(handler-case (some-user-function)
(file-error (condition) condition)
(division-by-zero () 0)
((or unbound-variable undefined-function) () 'unbound))
(handler-case (intern x y)
(error (condition) condition)
(:no-error (symbol status)
(declare (ignore symbol))
status))
[Macro]
ignore-errors {form}*
Executes its body in a context that handles conditions of type error by
returning control to this form. If no such condition is signaled, any values
returned by the last form are returned by ignore-errors. Otherwise, two values
are returned: nil and the error condition that was signaled.
ignore-errors could be defined by
(defmacro ignore-errors (&body forms)
`(handler-case (progn ,@forms)
(error (c) (values nil c)))
[Macro]
handler-bind ({(typespec handler)}*) {form}*
Executes body in a dynamic context where the given handler bindings are in
effect. Each typespec may be any type specifier. Each handler form should
evaluate to a function to be used to handle conditions of the given type(s)
during execution of the forms. This function should take a single argument, the
condition being signaled.
If more than one binding is specified, the bindings are searched sequentially
from top to bottom in search of a match (by visual analogy with typecase). If
an appropriate typespec is found, the associated handler is run in a context
where none of the handler bindings are visible (to avoid recursive errors). For
example, in the case of
(handler-bind ((unbound-variable #'(lambda ...))
(error #'(lambda ...)))
...)
if an unbound variable error is signaled in the body (and not handled by an
intervening handler), the first function will be called. If any other kind of
error is signaled, the second function will be called. In either case, neither
handler will be active while executing the code in the associated function.
[change_end]
-------------------------------------------------------------------------------
29.4.5. Defining Conditions
[change_begin]
[The contents of this section are still a subject of some debate within X3J13.
The reader may wish to take this section with a grain of salt, two aspirin
tablets, and call a hacker in the morning.-GLS]
[Macro]
define-condition name ({parent-type}*)
[({slot-specifier}*) {option}*]
Defines a new condition type called name, which is a subtype of each given
parent-type. Except as otherwise noted, the arguments are not evaluated.
Objects of this condition type will have all of the indicated slots, plus any
additional slots inherited from the parent types (its superclasses). If the
slots list is omitted, the empty list is assumed.
A slot must have the form
slot-specifier ::= slot-name | (slot-name [[?slot-option]])
For the syntax of a slot-option, see defclass. The slots of a condition object
are normal CLOS slots. Note that with-slots may be used instead of accessor
functions to access slots of a condition object.
make-condition will accept keywords (in the keyword package) with the print
name of any of the designated slots, and will initialize the corresponding
slots in conditions it creates.
Accessors are created according to the same rules as used by defclass.
The valid options are as follows:
(:documentation doc-string)
The doc-string should be either nil or a string that describes the purpose
of the condition type. If this option is omitted, nil is assumed. Calling
(documentation 'name 'type) will retrieve this information.
(:report exp)
If exp is not a literal string, it must be a suitable argument to the
function special form. The expression (function exp) will be evaluated in
the current lexical environment. It should produce a function of two
arguments, a condition and a stream, that prints on the stream a
description of the condition. This function is called whenever the
condition is printed while *print-escape* is nil.
If exp is a literal string, it is shorthand for
(lambda (c s)
(declare (ignore c))
(write-string exp s))
[That is, a function is provided that will simply write the given string
literally to the stream, regardless of the particular condition object
supplied.-GLS]
The :report option is processed after the new condition type has been
defined, so use of the slot accessors within the report function is
permitted. If this option is not specified, information about how to
report this type of condition will be inherited from the parent-type.
[X3J13 voted in March 1989 (ZLOS-CONDITIONS) to integrate the Condition
System and the Object System. In the original Condition System proposal,
define-condition allowed only one parent-type (the inheritance structure was a
simple hierarchy). Slot descriptions were much simpler, even simpler than those
for defstruct:
slot ::= slot-name | (slot-name) | (slot-name default-value)
Similarly, define-condition allowed a :conc-name option similar to that of
defstruct:
(:conc-name symbol-or-string)
Not now part of Common Lisp. As with defstruct, this sets up automatic
prefixing of the names of slot accessors. Also as in defstruct, the
default behavior is to use the name of the new type, name, followed by a
hyphen. (Generated names are interned in the package that is current at
the time that the define-condition is processed).
One consequence of the vote was to make define-condition slot descriptions like
those of defclass.-GLS]
Here are some examples of the use of define-condition.
The following form defines a condition of type peg/hole-mismatch that inherits
from a condition type called blocks-world-error:
(define-condition peg/hole-mismatch (blocks-world-error)
(peg-shape hole-shape)
(:report
(lambda (condition stream)
(with-slots (peg-shape hole-shape) condition
(format stream "A ~A peg cannot go in a ~A hole."
peg-shape hole-shape))))
The new type has slots peg-shape and hole-shape, so make-condition will accept
:peg-shape and :hole-shape keywords. The with-slots macro may be used to access
the peg-shape and hole-shape slots, as illustrated in the :report information.
Here is another example. This defines a condition called machine-error that
inherits from error:
(define-condition machine-error (error)
((machine-name
:reader machine-error-machine-name))
(:report (lambda (condition stream)
(format stream "There is a problem with ~A."
(machine-error-machine-name condition)))))
Building on this definition, we can define a new error condition that is a
subtype of machine-error for use when machines are not available:
(define-condition machine-not-available-error (machine-error) ()
(:report (lambda (condition stream)
(format stream "The machine ~A is not available."
(machine-error-machine-name condition)))))
We may now define a still more specific condition, built upon
machine-not-available-error, that provides a default for machine-name but does
not provide any new slots or report information. It just gives the machine-name
slot a default initialization:
(define-condition my-favorite-machine-not-available-error
(machine-not-available-error)
((machine-name :initform "MC.LCS.MIT.EDU")))
Note that since no :report clause was given, the information inherited from
machine-not-available-error will be used to report this type of condition.
[change_end]
-------------------------------------------------------------------------------
29.4.6. Creating Conditions
[change_begin]
The function make-condition is the basic means for creating condition objects.
[Function]
make-condition type &rest slot-initializations
Constructs a condition object of the given type using slot-initializations as a
specification of the initial value of the slots. The newly created condition is
returned.
The slot-initializations are alternating keyword/value pairs. For example:
(make-condition 'peg/hole-mismatch
:peg-shape 'square :hole-shape 'round)
[change_end]
-------------------------------------------------------------------------------
29.4.7. Establishing Restarts
[change_begin]
The lowest-level form that creates restart points is called restart-bind. The
restart-case macro is an abstraction that addresses many common needs for
restart-bind while offering a more palatable syntax. See also
with-simple-restart. The function that transfers control to a restart point
established by one of these macros is called invoke-restart.
All restarts have dynamic extent; a restart does not survive execution of the
form that establishes it.
[Macro]
with-simple-restart (name format-string {format-argument}*)
{form}*
This is shorthand for one of the most common uses of restart-case.
If the restart designated by name is not invoked while executing the forms, all
values returned by the last form are returned. If that restart is invoked,
control is transferred to the with-simple-restart form, which immediately
returns the two values nil and t.
The name may be nil, in which case an anonymous restart is established.
with-simple-restart could be defined by
(defmacro with-simple-restart ((restart-name format-string
&rest format-arguments)
&body forms)
`(restart-case (progn ,@forms)
(,restart-name ()
:report
(lambda (stream)
(format stream ,format-string ,@format-arguments))
(values nil t))))
Here is an example of the use of with-simple-restart.
Lisp> (defun read-eval-print-loop (level)
(with-simple-restart
(abort "Exit command level ~D." level)
(loop
(with-simple-restart
(abort "Return to command level ~D." level)
(let ((form (prog2 (fresh-line)
(read)
(fresh-line))))
(prin1 (eval form)))))))
=> READ-EVAL-PRINT-LOOP
Lisp> (read-eval-print-loop 1)
(+ 'a 3)
Error: The argument, A, to the function + was of the wrong type.
The function expected a number.
To continue, type :CONTINUE followed by an option number:
1: Specify a value to use this time.
2: Return to command level 1.
3: Exit command level 1.
4: Return to Lisp Toplevel.
Debug>
-------------------------------------------------------------------------------
Compatibility note: In contrast to the way that Zetalisp has traditionally
defined abort as a kind of condition to be handled, the Common Lisp Condition
System defines abort as a way to restart (``proceed'' in Zetalisp terms).
-------------------------------------------------------------------------------
Remark: Some readers may wonder what ought to be done by the ``abort'' key (or
whatever the implementation's interrupt key is-Control-C or Control-G, for
example). Such interrupts, whether synchronous or asynchronous in nature, are
beyond the scope of this chapter and indeed are not currently addressed by
Common Lisp at all. This may be a topic worth standardizing under separate
cover. Here is some speculation about some possible things that might happen.
An implementation might simply call abort or break directly without signaling
any condition.
Another implementation might signal some condition related to the fact that a
key had been pressed rather than to the action that should be taken. This is
one way to allow user customization. Perhaps there would be an
implementation-dependent keyboard-interrupt condition type with a slot
containing the key that was pressed-or perhaps there would be such a condition
type, but rather than its having slots, different subtypes of that type with
names like keyboard-abort, keyboard-break, and so on might be signaled. That
implementation would then document the action it would take if user programs
failed to handle the condition, and perhaps ways for user programs to usefully
dismiss the interrupt.
-------------------------------------------------------------------------------
Implementation note: Implementors are encouraged to make sure that there is
always a restart named abort around any user code so that user code can call
abort at any time and expect something reasonable to happen; exactly what the
reasonable thing is may vary somewhat. Typically, in an interactive program,
invoking abort should return the user to top level, though in some batch or
multi-processing situations killing the running process might be more
appropriate.
-------------------------------------------------------------------------------
[Macro]
restart-case expression {(case-name arglist
{keyword value}*
{form}*)}*
The expression is evaluated in a dynamic context where the clauses have special
meanings as points to which control may be transferred. If the expression
finishes executing and returns any values, all such values are simply returned
by the restart-case form. While the expression is running, any code may
transfer control to one of the clauses (see invoke-restart). If a transfer
occurs, the forms in the body of that clause will be evaluated and any values
returned by the last such form will be returned by the restart-case form.
As a special case, if the expression is a list whose car is signal, error,
cerror, or warn, then with-condition-restarts is implicitly used to associate
the restarts with the condition to be signaled. For example,
(restart-case (signal weird-error)
(become-confused ...)
(rewind-line-printer ...)
(halt-and-catch-fire ...))
is equivalent to
(restart-case (with-condition-restarts
weird-error
(list (find-restart 'become-confused)
(find-restart 'rewind-line-printer)
(find-restart 'halt-and-catch-fire))
(signal weird-error))
(become-confused ...)
(rewind-line-printer ...)
(halt-and-catch-fire ...))
If there are no forms in a selected clause, restart-case returns nil.
The case-name may be nil or a symbol naming this restart.
It is possible to have more than one clause use the same case-name. In this
case, the first clause with that name will be found by find-restart. The other
clauses are accessible using compute-restarts. [In this respect, restart-case
is rather different from case!-GLS]
Each arglist is a normal lambda-list containing parameters to be bound during
the execution of its corresponding forms. These parameters are used to pass any
necessary data from a call to invoke-restart to the restart-case clause.
By default, invoke-restart-interactively will pass no arguments and all
parameters must be optional in order to accommodate interactive restarting.
However, the parameters need not be optional if the :interactive keyword has
been used to inform invoke-restart-interactively about how to compute a proper
argument list.
The valid keyword value pairs are the following:
:test fn
The fn must be a suitable argument for the function special form. The
expression (function fn) will be evaluated in the current lexical
environment. It should produce a function of one argument, a condition. If
this function returns nil when given some condition, functions such as
find-restart, compute-restart, and invoke-restart will not consider this
restart when searching for restarts associated with that condition. If
this pair is not supplied, it is as if
(lambda (c) (declare (ignore c)) t)
were used for the fn.
:interactive fn
The fn must be a suitable argument for the function special form. The
expression (function fn) will be evaluated in the current lexical
environment. It should produce a function of no arguments that returns
arguments to be used by invoke-restart-interactively when invoking this
function. This function will be called in the dynamic environment
available prior to any restart attempt. It may interact with the user on
the stream in *query-io*.
If a restart is invoked interactively but no :interactive option was
supplied, the argument list used in the invocation is the empty list.
:report exp
If exp is not a literal string, it must be a suitable argument to the
function special form. The expression (function exp) will be evaluated in
the current lexical environment. It should produce a function of one
argument, a stream, that prints on the stream a description of the
restart. This function is called whenever the restart is printed while
*print-escape* is nil.
If exp is a literal string, it is shorthand for
(lambda (s) (write-string exp s))
[That is, a function is provided that will simply write the given string
literally to the stream.-GLS]
If a named restart is asked to report but no report information has been
supplied, the name of the restart is used in generating default report
text.
When *print-escape* is nil, the printer will use the report information
for a restart. For example, a debugger might announce the action of typing
``:continue'' by executing the equivalent of
(format *debug-io* "~&~S - ~A~%" ':continue some-restart)
which might then display as something like
:CONTINUE - Return to command level.
It is an error if an unnamed restart is used and no report information is
provided.
-------------------------------------------------------------------------------
Rationale: Unnamed restarts are required to have report information on the
grounds that they are generally only useful interactively, and an interactive
option that has no description is of little value.
-------------------------------------------------------------------------------
Implementation note: Implementations are encouraged to warn about this error at
compilation time.
At run time, this error might be noticed when entering the debugger. Since
signaling an error would probably cause recursive entry into the debugger
(causing yet another recursive error, and so on), it is suggested that the
debugger print some indication of such problems when they occur, but not
actually signal errors.
-------------------------------------------------------------------------------
Note that
(restart-case expression
(name1 arglist1 options1 . body1)
(name2 arglist2 options2 . body2)
...)
is essentially equivalent to
(block #1=#:block-1
(let ((#2=#:var-2 nil))
(tagbody
(restart-bind ((name1 #'(lambda (&rest temp)
(setq #2# temp)
(go #3=#:tag-3))
<slightly transformed options1>)
(name2 #'(lambda (&rest temp)
(setq #2# temp)
(go #4=#:tag-4))
<slightly transformed options2>)
...)
(return-from #1# expression))
#3# (return-from #1#
(apply #'(lambda arglist1 . body1) #2#))
#4# (return-from #1#
(apply #'(lambda arglist2 . body2) #2#))
...)))
[Note the use of ``gensyms'' such as #:block-1 as block names, variables, and
tagbody tags in this example, and the use of #n= and #n# read-macro syntax to
indicate that the very same gensym appears in multiple places.-GLS]
Here are some examples of the use of restart-case.
(loop
(restart-case (return (apply function some-args))
(new-function (new-function)
:report "Use a different function."
:interactive
(lambda ()
(list (prompt-for 'function "Function: ")))
(setq function new-function))))
(loop
(restart-case (return (apply function some-args))
(nil (new-function)
:report "Use a different function."
:interactive
(lambda ()
(list (prompt-for 'function "Function: ")))
(setq function new-function))))
(restart-case (a-command-loop)
(return-from-command-level ()
:report
(lambda (s) ;Argument s is a stream
(format s "Return from command level ~D." level))
nil))
(loop
(restart-case (another-random-computation)
(continue () nil)))
The first and second examples are equivalent from the point of view of someone
using the interactive debugger, but they differ in one important aspect for
non-interactive handling. If a handler ``knows about'' named restarts, as in,
for example,
(when (find-restart 'new-function)
(invoke-restart 'new-function the-replacement))
then only the first example, and not the second, will have control transferred
to its correction clause, since only the first example uses a restart named
new-function.
Here is a more complete example:
(let ((my-food 'milk)
(my-color 'greenish-blue))
(do ()
((not (bad-food-color-p my-food my-color)))
(restart-case (error 'bad-food-color
:food my-food :color my-color)
(use-food (new-food)
:report "Use another food."
(setq my-food new-food))
(use-color (new-color)
:report "Use another color."
(setq my-color new-color))))
;; We won't get to here until MY-FOOD
;; and MY-COLOR are compatible.
(list my-food my-color))
Assuming that use-food and use-color have been defined as
(defun use-food (new-food)
(invoke-restart 'use-food new-food))
(defun use-color (new-color)
(invoke-restart 'use-color new-color))
a handler can then restart from the error in either of two ways. It may correct
the color or correct the food. For example:
#'(lambda (c) ... (use-color 'white) ...) ;Corrects color
#'(lambda (c) ... (use-food 'cheese) ...) ;Corrects food
Here is an example using handler-bind and restart-case that refers to a
condition type foo-error, presumably defined elsewhere:
(handler-bind ((foo-error #'(lambda (ignore) (use-value 7))))
(restart-case (error 'foo-error)
(use-value (x) (* x x))))
=> 49
[Macro]
restart-bind ({(name function {keyword value}*)}*) {form}*
Executes a body of forms in a dynamic context where the given restart bindings
are in effect.
Each name may be nil to indicate an anonymous restart, or some other symbol to
indicate a named restart.
Each function is a form that should evaluate to a function to be used to
perform the restart. If invoked, this function may either perform a non-local
transfer of control or it may return normally. The function may take whatever
arguments the programmer feels are appropriate; it will be invoked only if
invoke-restart is used from a program, or if a user interactively asks the
debugger to invoke it. In the case of interactive invocation, the
:interactive-function option is used.
The valid keyword value pairs are as follows:
:test-function form
The form will be evaluated in the current lexical environment and should
return a function of one argument, a condition. If this function returns
nil when given some condition, functions such as find-restart,
compute-restart, and invoke-restart will not consider this restart when
searching for restarts associated with that condition. If this pair is not
supplied, it is as if
#'(lambda (c) (declare (ignore c)) t)
were used for the form.
:interactive-function form
The form will be evaluated in the current lexical environment and should
return a function of no arguments that constructs a list of arguments to
be used by invoke-restart-interactively when invoking this restart. The
function may prompt interactively using *query-io* if necessary.
:report-function form
The form will be evaluated in the current lexical environment and should
return a function of one argument, a stream, that prints on the stream a
summary of the action this restart will take. This function is called
whenever the restart is printed while *print-escape* is nil.
[Macro]
with-condition-restarts condition-form restarts-form
{declaration}* {form}*
The value of condition-form should be a condition C and the value of
restarts-form should be a list of restarts (R1 R2 ...). The forms of the body
are evaluated as an implicit progn. While in the dynamic context of the body,
an attempt to find a restart associated with a particular condition C' will
consider the restarts R1, R2, ... if C' is eq to C.
Usually this macro is not used explicitly in code, because restart-case handles
most of the common uses in a way that is syntactically more concise.
[The X3J13 vote (CONDITION-RESTARTS) left it unclear whether
with-condition-restarts permits declarations to appear at the heads of its
body. I believe that was the intent, but this is only my interpretation.-GLS]
[change_end]
-------------------------------------------------------------------------------
29.4.8. Finding and Manipulating Restarts
[change_begin]
The following functions determine what restarts are active and invoke restarts.
[Function]
compute-restarts &optional condition
Uses the dynamic state of the program to compute a list of the restarts that
are currently active. See restart-bind.
If condition is nil or not supplied, all outstanding restarts are returned. If
condition is not nil, only restarts associated with that condition are
returned.
Each restart represents a function that can be called to perform some form of
recovery action, usually a transfer of control to an outer point in the running
program. Implementations are free to implement these objects in whatever manner
is most convenient; the objects need have only dynamic extent (relative to the
scope of the binding form that instantiates them).
The list that results from a call to compute-restarts is ordered so that the
inner (that is, more recently established) restarts are nearer the head of the
list.
Note, too, that compute-restarts returns all valid restarts, including
anonymous ones, even if some of them have the same name as others and would
therefore not be found by find-restart when given a symbol argument.
Implementations are permitted, but not required, to return different (that is,
non-eq) lists from repeated calls to compute-restarts while in the same dynamic
environment. It is an error to modify the list that is returned by
compute-restarts.
[Function]
restart-name restart
Returns the name of the given restart, or nil if it is not named.
[Function]
find-restart restart-identifier &optional condition
Searches for a particular restart in the current dynamic environment.
If condition is nil or not supplied, all outstanding restarts are considered.
If condition is not nil, only restarts associated with that condition are
considered.
If the restart-identifier is a non-nil symbol, then the innermost (that is,
most recently established) restart with that name is returned; nil is returned
if no such restart is found.
If restart-identifier is a restart object, then it is simply returned, unless
it is not currently active, in which case nil is returned.
Although anonymous restarts have a name of nil, it is an error for the symbol
nil to be given as the restart-identifier. Applications that would seem to
require this should be rewritten to make appropriate use of compute-restarts
instead.
[Function]
invoke-restart restart-identifier &rest arguments
Calls the function associated with the given restart-identifier, passing any
given arguments. The restart-identifier must be a restart or the non-null name
of a restart that is valid in the current dynamic context. If the argument is
not valid, an error of type control-error will be signaled.
-------------------------------------------------------------------------------
Implementation note: Restart functions call this function, not vice versa.
-------------------------------------------------------------------------------
[Function]
invoke-restart-interactively restart-identifier
Calls the function associated with the given restart-identifier, prompting for
any necessary arguments. The restart-identifier must be a restart or the
non-null name of a restart that is valid in the current dynamic context. If the
argument is not valid, an error of type control-error will be signaled.
The function invoke-restart-interactively will prompt for arguments by
executing the code provided in the :interactive keyword to restart-case or
:interactive-function keyword to restart-bind.
If no :interactive or :interactive-function option has been supplied in the
corresponding restart-case or restart-bind, then it is an error if the restart
takes required arguments. If the arguments are optional, an empty argument list
will be used in this case.
Once invoke-restart-interactively has calculated the arguments, it simply
performs (apply #'invoke-restart restart-identifier arguments).
invoke-restart-interactively is used internally by the debugger and may also be
useful in implementing other portable, interactive debugging tools.
[change_end]
-------------------------------------------------------------------------------
29.4.9. Warnings
[change_begin]
Warnings are a subclass of errors that are conventionally regarded as ``mild.''
[Function]
warn datum &rest arguments
[This supersedes the description of warn given in section 24.1.-GLS]
Warns about a situation, by signaling a condition of type warning.
If datum is a condition, then that condition is used directly. In this case, if
the condition is not of type warning or arguments is non-nil, an error of type
type-error is signaled.
If datum is a condition type (a class or class name), then the condition used
is effectively the result of (apply #'make-condition datum arguments). This
result must be of type warning or an error of type type-error is signaled.
If datum is a string, then the condition used is effectively the result of
(make-condition 'simple-error
:format-string datum
:format-arguments arguments)
The precise mechanism for warning is as follows.
1. The warning condition is signaled.
While the warning condition is being signaled, the muffle-warning restart
is established for use by a handler to bypass further action by warn (that
is, to cause warn to immediately return nil).
As part of the signaling process, if (typep condition *break-on-signals*)
is true, then a break will occur prior to beginning the signaling process.
2. If no handlers for the warning condition are found, or if all such
handlers decline, then the condition will be reported to *error-output* by
the warn function (with possible implementation-specific extra output such
as motion to a fresh line before or after the display of the warning, or
supplying some introductory text mentioning the name of the function that
called warn or the fact that this is a warning).
3. The value returned by warn (if it returns) is nil.
[change_end]
-------------------------------------------------------------------------------
29.4.10. Restart Functions
[change_begin]
Common Lisp has the following restart functions built in.
[Function]
abort &optional condition
This function transfers control to the restart named abort. If no such restart
exists, abort signals an error of type control-error.
If condition is nil or not supplied, all outstanding restarts are considered.
If condition is not nil, only restarts associated with that condition are
considered.
The purpose of the abort restart is generally to allow control to return to the
innermost ``command level.''
[Function]
continue &optional condition
This function transfers control to the restart named continue. If no such
restart exists, continue returns nil.
If condition is nil or not supplied, all outstanding restarts are considered.
If condition is not nil, only restarts associated with that condition are
considered.
The continue restart is generally part of simple protocols where there is a
single ``obvious'' way to continue, as with break and cerror. Some user-defined
protocols may also wish to incorporate it for similar reasons. In general,
however, it is more reliable to design a special-purpose restart with a name
that better suits the particular application.
[Function]
muffle-warning &optional condition
This function transfers control to the restart named muffle-warning. If no such
restart exists, muffle-warning signals an error of type control-error.
If condition is nil or not supplied, all outstanding restarts are considered.
If condition is not nil, only restarts associated with that condition are
considered.
warn sets up this restart so that handlers of warning conditions have a way to
tell warn that a warning has already been dealt with and that no further action
is warranted.
[Function]
store-value value &optional condition
This function transfers control (and one value) to the restart named
store-value. If no such restart exists, store-value returns nil.
If condition is nil or not supplied, all outstanding restarts are considered.
If condition is not nil, only restarts associated with that condition are
considered.
The store-value restart is generally used by handlers trying to recover from
errors of types such as cell-error or type-error, where the handler may wish to
supply a replacement datum to be stored permanently.
[Function]
use-value value &optional condition
This function transfers control (and one value) to the restart named use-value.
If no such restart exists, use-value returns nil.
If condition is nil or not supplied, all outstanding restarts are considered.
If condition is not nil, only restarts associated with that condition are
considered.
The use-value restart is generally used by handlers trying to recover from
errors of types such as cell-error, where the handler may wish to supply a
replacement datum for one-time use.
[change_end]
-------------------------------------------------------------------------------
29.4.11. Debugging Utilities
[change_begin]
Common Lisp does not specify exactly what a debugger is or does, but it does
provide certain means for indicating intent to transfer control to a
supervisory or debugging facility.
[Function]
break &optional format-string &rest format-arguments
[This supersedes the description of break given in section 24.1.-GLS]
The function break prints the message described by the format-string and
format-arguments and then goes directly into the debugger without allowing any
possibility of interception by programmed error-handling facilities.
If no format-string is supplied, a suitable default will be generated.
If continued, break returns nil.
Note that break is presumed to be used as a way of inserting temporary
debugging ``breakpoints'' in a program, not as a way of signaling errors; it is
expected that continuing from a break will not trigger any unusual recovery
action. For this reason, break does not take the additional format control
string that cerror takes as its first argument. This and the lack of any
possibility of interception by programmed error handling are the only
program-visible differences between break and cerror. The user interface
aspects of these functions are permitted to vary more widely; for example, it
is permissible for a read-eval-print loop to be entered by break rather than by
the conventional debugger.
break could be defined by
(defun break (&optional (format-string "Break")
&rest format-arguments)
(with-simple-restart (continue "Return from BREAK.")
(invoke-debugger
(make-condition 'simple-condition
:format-string format-string
:format-arguments format-arguments)))
nil)
[Function]
invoke-debugger condition
Attempts interactive handling of its argument, which must be a condition.
If the variable *debugger-hook* is not nil, it will be called as a function on
two arguments: the condition being handled and the value of *debugger-hook*. If
a hook function returns normally, the standard debugger will be tried.
The standard debugger will never directly return. Return can occur only by a
special transfer of control, such as the use of a restart.
-------------------------------------------------------------------------------
Remark: The exact way in which the debugger interacts with users is expected to
vary considerably from system to system. For example, some systems may use a
keyboard interface, while others may use a mouse interface. Of those systems
using keyboard commands, some may use single-character commands and others may
use parsed line-at-a-time commands. The exact set of commands will vary as
well. The important properties of a debugger are that it makes information
about the error accessible and that it makes the set of apparent restarts
easily accessible.
It is desirable to have a mode where the debugger allows other features, such
as the ability to inspect data, stacks, etc. However, it may sometimes be
appropriate to have this kind of information hidden from users. Experience on
the Lisp Machines has shown that some users who are not programmers develop a
terrible phobia of debuggers. The reason for this usually may be traced to the
fact that the debugger is very foreign to them and provides an overwhelming
amount of information of interest only to programmers. With the advent of
restarts, there is a clear mechanism for the construction of ``friendly''
debuggers. Programmers can be taught how to get to the information they need
for debugging, but it should be possible to construct user interfaces to the
debugger that are natural, convenient, intelligible, and friendly even to
non-programmers.
-------------------------------------------------------------------------------
[Variable]
*debugger-hook*
This variable should hold either nil or a function of two arguments, a
condition and the value of *debugger-hook*. This function may either handle the
condition (transfer control) or return normally (allowing the standard debugger
to run).
Note that, to minimize recursive errors while debugging, *debugger-hook* is
bound to nil when calling this function. When evaluating code typed in by the
user interactively, the hook function may want to bind *debugger-hook* to the
function that was its second argument so that recursive errors can be handled
using the same interactive facility.
[change_end]
-------------------------------------------------------------------------------
29.5. Predefined Condition Types
[change_begin]
[The proposal for the Common Lisp Condition System introduced a new notation
for documenting types, treating them in the same syntactic manner as functions
and variables. This notation is used in this section but is not reflected
throughout the entire book.-GLS]
X3J13 voted in March 1989 (ZLOS-CONDITIONS) to integrate the Condition System
and the Object System. All condition types are CLOS classes and all condition
objects are ordinary CLOS objects.
[Type]
restart
This is the data type used to represent a restart.
----------------------------------------------------------------
Table 29-1: Condition Type Hierarchy
condition
simple-condition
serious-condition
error
simple-error
arithmetic-error
division-by-zero
floating-point-overflow
floating-point-underflow
...
cell-error
unbound-variable
undefined-function
...
control-error
file-error
package-error
program-error
stream-error
end-of-file
...
type-error
simple-type-error
...
...
storage-condition
...
warning
simple-warning
...
...
----------------------------------------------------------------
The Common Lisp condition type hierarchy is illustrated in table 29-1.
The types that are not leaves in the hierarchy (that is, condition, warning,
storage-condition, error, arithmetic-error, control-error, and so on) are
provided primarily for type inclusion purposes. Normally they would not be
directly instantiated.
Implementations are permitted to support non-portable synonyms for these types,
as well as to introduce other types that are above, below, or between the types
shown in this tree as long as the indicated subtype relationships are not
violated.
The types simple-condition, serious-condition, and warning are pairwise
disjoint. The type error is also disjoint from types simple-condition and
warning.
[Type]
condition
All types of conditions, whether error or non-error, must inherit from this
type.
[Type]
warning
All types of warnings should inherit from this type. This is a subtype of
condition.
[Type]
serious-condition
All serious conditions (conditions serious enough to require interactive
intervention if not handled) should inherit from this type. This is a subtype
of condition.
This condition type is provided primarily for terminological convenience. In
fact, signaling a condition that inherits from serious-condition does not force
entry into the debugger. Rather, it is conventional to use error (or something
built on error) to signal conditions that are of this type, and to use signal
to signal conditions that are not of this type.
[Type]
error
All types of error conditions inherit from this condition. This is a subtype of
serious-condition.
The default condition type for signal and warn is simple-condition. The default
condition type for error and cerror is simple-error.
[Type]
simple-condition
Conditions signaled by signal when given a format string as a first argument
are of this type. This is a subtype of condition. The initialization keywords
:format-string and :format-arguments are supported to initialize the slots,
which can be accessed using simple-condition-format-string and
simple-condition-format-arguments. If :format-arguments is not supplied to
make-condition, the format-arguments slot defaults to nil.
[Type]
simple-warning
Conditions signaled by warn when given a format string as a first argument are
of this type. This is a subtype of warning. The initialization keywords
:format-string and :format-arguments are supported to initialize the slots,
which can be accessed using simple-condition-format-string and
simple-condition-format-arguments. If :format-arguments is not supplied to
make-condition, the format-arguments slot defaults to nil.
In implementations supporting multiple inheritance, this type will also be a
subtype of simple-condition.
[Type]
simple-error
Conditions signaled by error and cerror when given a format string as a first
argument are of this type. This is a subtype of error. The initialization
keywords :format-string and :format-arguments are supported to initialize the
slots, which can be accessed using simple-condition-format-string and
simple-condition-format-arguments. If :format-arguments is not supplied to
make-condition, the format-arguments slot defaults to nil.
In implementations supporting multiple inheritance, this type will also be a
subtype of simple-condition.
[Function]
simple-condition-format-string condition
Accesses the format-string slot of a given condition, which must be of type
simple-condition, simple-warning, simple-error, or simple-type-error.
[Function]
simple-condition-format-arguments condition
Accesses the format-arguments slot of a given condition, which must be of type
simple-condition, simple-warning, simple-error, or simple-type-error.
[Type]
storage-condition
Conditions that relate to storage overflow should inherit from this type. This
is a subtype of serious-condition.
[Type]
type-error
Errors in the transfer of data in a program should inherit from this type. This
is a subtype of error. For example, conditions to be signaled by check-type
should inherit from this type. The initialization keywords :datum and
:expected-type are supported to initialize the slots, which can be accessed
using type-error-datum and type-error-expected-type.
[Function]
type-error-datum condition
Accesses the datum slot of a given condition, which must be of type type-error.
[Function]
type-error-expected-type condition
Accesses the expected-type slot of a given condition, which must be of type
type-error. Users of type-error conditions are expected to fill this slot with
an object that is a valid Common Lisp type specifier.
[Type]
simple-type-error
Conditions signaled by facilities similar to check-type may want to use this
type. The initialization keywords :format-string and :format-arguments are
supported to initialize the slots, which can be accessed using
simple-condition-format-string and simple-condition-format-arguments. If
:format-arguments is not supplied to make-condition, the format-arguments slot
defaults to nil.
In implementations supporting multiple inheritance, this type will also be a
subtype of simple-condition.
[Type]
program-error
Errors relating to incorrect program syntax that are statically detectable
should inherit from this type (regardless of whether they are in fact
statically detected). This is a subtype of error. This is not a subtype of
control-error.
[Type]
control-error
Errors in the dynamic transfer of control in a program should inherit from this
type. This is a subtype of error. This is not a subtype of program-error.
The errors that result from giving throw a tag that is not active or from
giving go or return-from a tag that is no longer dynamically available are
control errors.
On the other hand, the errors that result from naming a go tag or return-from
tag that is not lexically apparent are not control errors. They are program
errors. See program-error.
[Type]
package-error
Errors that occur during operations on packages should inherit from this type.
This is a subtype of error. The initialization keyword :package is supported to
initialize the slot, which can be accessed using package-error-package.
[Function]
package-error-package condition
Accesses the package (or package name) that was being modified or manipulated
in a condition of type package-error.
[Type]
stream-error
Errors that occur during input from, output to, or closing a stream should
inherit from this type. This is a subtype of error. The initialization keyword
:stream is supported to initialize the slot, which can be accessed using
stream-error-stream.
[Function]
stream-error-stream condition
Accesses the offending stream of a condition of type stream-error.
[Type]
end-of-file
The error that results when a read operation is done on a stream that has no
more tokens or characters should inherit from this type. This is a subtype of
stream-error.
[Type]
file-error
Errors that occur during an attempt to open a file, or during some low-level
transaction with a file system, should inherit from this type. This is a
subtype of error. The initialization keyword :pathname is supported to
initialize the slot, which can be accessed using file-error-pathname.
[Function]
file-error-pathname condition
Accesses the offending pathname of a condition of type file-error.
[Type]
cell-error
Errors that occur while accessing a location should inherit from this type.
This is a subtype of error. The initialization keyword :name is supported to
initialize the slot, which can be accessed using cell-error-name.
[Function]
cell-error-name condition
Accesses the offending cell name of a condition of type cell-error.
[Type]
unbound-variable
The error that results from trying to access the value of an unbound variable
should inherit from this type. This is a subtype of cell-error.
[Type]
undefined-function
The error that results from trying to access the value of an undefined function
should inherit from this type. This is a subtype of cell-error.
-------------------------------------------------------------------------------
Remark: [Note: This remark was written well before the vote by X3J13 in June
1988 (CLOS) to add the Common Lisp Object System to the forthcoming draft
standard (see chapter 28) and the vote to integrate the Condition System and
the Object System. I have retained the remark here for reasons of historical
interest.-GLS]
Some readers may wonder why undefined-function is not defined to inherit from
some condition such as control-error. The answer is that any such arrangement
would require the presence of multiple inheritance-a luxury we do not currently
have (without resorting to deftype, which we are currently avoiding). When the
Common Lisp Object System comes into being, we might want to consider issues
like this. Multiple inheritance makes a lot of things in a condition system
much more flexible to deal with.
-------------------------------------------------------------------------------
[Type]
arithmetic-error
Errors that occur while doing arithmetic type operations should inherit from
this type. This is a subtype of error. The initialization keywords :operation
and :operands are supported to initialize the slots, which can be accessed
using arithmetic-error-operation and arithmetic-error-operands.
[Function]
arithmetic-error-operation condition
Accesses the offending operation of a condition of type arithmetic-error.
[Function]
arithmetic-error-operands condition
Accesses a list of the offending operands in a condition of type
arithmetic-error.
[Type]
division-by-zero
Errors that occur because of division by zero should inherit from this type.
This is a subtype of arithmetic-error.
[Type]
floating-point-overflow
Errors that occur because of floating-point overflow should inherit from this
type. This is a subtype of arithmetic-error.
[Type]
floating-point-underflow
Errors that occur because of floating-point underflow should inherit from this
type. This is a subtype of arithmetic-error.
[change_end]
-------------------------------------------------------------------------------